package com.wfm.limitalgor;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Data
public class SlidingWindowRateLimiter {
    //时间窗口大小，单位毫秒
    long windowSize;
    //允许通过的请求数
    int maxRequestCount;
    //各个窗口内请求计数
    int[] shardRequestCount;
    //窗口右边界
    long windowBorder;
    //分片窗口数
    int shardNum;
    //请求总数
    int totalCount;
    //当前窗口下标
    int shardId;
    //每个小窗口大小，毫秒
    long tinyWindowSize;


    public SlidingWindowRateLimiter(long windowSize, int shardNum, int maxRequestCount) {
        this.windowSize = windowSize;
        this.shardNum = shardNum;
        this.maxRequestCount = maxRequestCount;
        this.shardRequestCount = new int[shardNum];
        this.tinyWindowSize = windowSize / shardNum;
        this.windowBorder = System.currentTimeMillis();
    }
    public synchronized boolean tryAcquire() {
        long currentTime = System.currentTimeMillis();
        if (windowBorder < currentTime) {
            log.info("window reset");
            do {
                shardId = (++shardId) % shardNum;
                totalCount -= shardRequestCount[shardId];
                shardRequestCount[shardId] = 0;
                windowBorder += tinyWindowSize;
            } while (windowBorder < currentTime);
        }

        if (totalCount < maxRequestCount) {
            log.info("tryAcquire success:{}", shardId);
            shardRequestCount[shardId]++;
            totalCount++;
            return true;
        } else {
            log.info("tryAcquire fail");
            return false;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        test2();
    }

    private static void test1() throws InterruptedException {
        SlidingWindowRateLimiter limiter = new SlidingWindowRateLimiter(1000, 2,3);
        for (int i = 0; i < 4; i++) {
            System.out.println("================="+i+"=============");
            switch (i){
                case 0:
                    for (int j = 0; j < 3; j++) {
                        limiter.tryAcquire();
                    }
                    break;
                case 1:
                    for (int j = 0; j < 4; j++) {
                        limiter.tryAcquire();
                    }
                    break;
                case 2:
                    for (int j = 0; j < 5; j++) {
                        limiter.tryAcquire();
                    }
                    break;
                case 3:
                    for (int j = 0; j < 2; j++) {
                        limiter.tryAcquire();
                    }
                    break;
                default:
                    break;
            }

            Thread.sleep(1000);

        }
    }

    /**
     * 总共4个0.5s，就是2s
     * 第一个0.5s：不请求
     * 第二个0.5s: 请求3个
     * 第三个0.5s：请求3个
     * 第四个0.5s：不请求
     * 0__1__2__3
     * 第1和第2，总共请求了6次，但是
     *
     */
    private static void test2() throws InterruptedException {
        SlidingWindowRateLimiter limiter = new SlidingWindowRateLimiter(1000, 2,3);;
        for (int i = 0; i < 6; i++) {
            System.out.println("================="+i+"=============");
            switch (i){
                case 0:
                    break;
                case 1:
                    for (int j = 0; j < 3; j++) {
                        limiter.tryAcquire();
                    }
                    break;
                case 5:
                    for (int j = 0; j < 6; j++) {
                        limiter.tryAcquire();
                    }
                    break;
                default:
                    break;
            }

            Thread.sleep(500);

        }
    }
}